/**
 * Created by udi on 17-7-15.
 */
'use strict';

const fs = require('fs');
const path = require('path');

/*

 递归处理文件,文件夹

 path 路径
 floor 层数
 handleFile 文件,文件夹处理函数

 */
Object.defineProperty(String.prototype, 'format', {
  value: function () {
    var json = arguments[0];
    var setEmptyifNo = arguments[1] || false;
    var left = arguments[2] || "{", right = arguments[3] || "}"

    return this
        .replace(left + left, String.fromCharCode(7), "g")
        .replace(right + right, String.fromCharCode(8), "g")
        .replace(new RegExp(left + "(\\w+)" + right, "g"),
            //m 是指搜索到的整个部分， 如： {id} , 而 i  是指 该部分的分组内容 ， 如 id
            function (m, i) {
              if (i in json) {
                var value = json[i];
                if (value || (value === 0) || ( value === false)) {
                  return value;
                }
                else {
                  return "";
                }
              }
              else if (setEmptyifNo) return "";
              else return m;
            })
        .replace(String.fromCharCode(7), left, "g")
        .replace(String.fromCharCode(8), right, "g")
        ;
  }, enumerable: false
});


//"abc,def".indexOfExt(0,true,"d","ef") === { stringIndex: 4 , matchedValue: "d", matchedIndex: 2 }
Object.defineProperty(String.prototype, 'indexOfExt', {
  value: function () {
    if (typeof arguments[1] != "boolean") {
      throw new Error("第二个参数必须是布尔值，表示是否忽略大小写比较")
    }
    var ignoreCase = arguments[1] || false;
    var toFind = [];
    var findFlag = {}; //key是各个值的index，value为在整个字符串的开始匹配的位置。

    if (arguments.length == 3 && (arguments[2] instanceof Array)) {
      toFind = arguments[2];

      for (var i = 0; i < toFind.length; i++) {
        findFlag[i] = 0;
      }
    }
    else {
      for (var i = 2; i < arguments.length; i++) {
        toFind.push(arguments[i]);
        findFlag[i - 2] = 0;
      }
    }


    for (var stringIndex = arguments[0], len = this.length; stringIndex < len; stringIndex++) {
      var crrentChar = this[stringIndex];

      for (var itemIndex = 0; itemIndex < toFind.length; itemIndex++) {
        var item = toFind[itemIndex];
        var itemPos = findFlag[itemIndex];

        if (item[itemPos] !== crrentChar) {
          if (!ignoreCase || (item[itemPos].toLowerCase() !== crrentChar.toLowerCase())) {
            findFlag[itemIndex] = 0
            continue;
          }
        }

        findFlag[itemIndex]++;

        //如果匹配了。
        if (findFlag[itemIndex] == item.length) {
          return {stringIndex: stringIndex - item.length + 1, matchedValue: item, matchedIndex: itemIndex};
        }
      }
    }
    return false;
  }, enumerable: false
});

//往前找最近的一个
Object.defineProperty(String.prototype, 'indexOfPrevExt', {
  value: function () {
    if (typeof arguments[1] != "boolean") {
      throw new Error("第二个参数必须是布尔值，表示是否忽略大小写比较")
    }
    var ignoreCase = arguments[1] || false;
    var toFind = [];
    var findFlag = {}; //key是各个值的index，value为在整个字符串的开始匹配的位置。

    if (arguments.length == 3 && (arguments[2] instanceof Array)) {
      toFind = arguments[2];

      for (var i = 0; i < toFind.length; i++) {
        findFlag[i] = toFind[i].length;
      }
    }
    else {
      for (var i = 2; i < arguments.length; i++) {
        toFind.push(arguments[i]);
        findFlag[i - 2] = arguments[i].length;
      }
    }


    for (var stringIndex = Math.min(arguments[0], this.length - 1); stringIndex >= 0; stringIndex--) {
      var crrentChar = this[stringIndex];

      for (var itemIndex = 0; itemIndex < toFind.length; itemIndex++) {
        var item = toFind[itemIndex];
        var itemPos = findFlag[itemIndex];

        if (item[itemPos - 1] !== crrentChar) {
          if (!ignoreCase || (item[itemPos - 1].toLowerCase() !== crrentChar.toLowerCase())) {
            findFlag[itemIndex] = item.length
            continue;
          }
        }

        findFlag[itemIndex]--;

        //如果匹配了。
        if (findFlag[itemIndex] == 0) {
          return {stringIndex: stringIndex - item.length + 1, matchedValue: item, matchedIndex: itemIndex};
        }
      }
    }
    return false;
  }, enumerable: false
});


Object.defineProperty(Array.prototype, 'contains', {
  value: function (index, value) {
    if (value === undefined) {
      value = index;
      index = 0;
    }
    var isFunction = value instanceof Function;
    for (var i = 0, len = this.length; i < len; i++) {
      var item = this[i];
      if (isFunction) {
        if (value(item)) return true;
      }
      else {
        if (item == value) return true;
      }
    }
    return false;
  }, enumerable: false
});


Object.defineProperty(Date.prototype, "toString", {
  value: function (format) {
    if (!this) return "";
    format = format || "yyyy-MM-dd HH:mm:ss";
    var time = this;
    var t = new Date(time);
    var tf = function (i) {
      return (i < 10 ? '0' : '') + i
    };
    return format.replace(/yyyy|MM|dd|HH|mm|ss/g, function (a) {
      switch (a) {
        case 'yyyy':
          return tf(t.getFullYear());
          break;
        case 'MM':
          return tf(t.getMonth() + 1);
          break;
        case 'mm':
          return tf(t.getMinutes());
          break;
        case 'dd':
          return tf(t.getDate());
          break;
        case 'HH':
          return tf(t.getHours());
          break;
        case 'ss':
          return tf(t.getSeconds());
          break;
      }
    })
  }, enumerable: false
});

let AsBool = function (value) {
  if (!value) return false;
  if (value === "0") return false;
  if (value === "false") return false;
  if (value === "False") return false;
  if (value === "FALSE") return false;
  return true;
}

let walk = async function (path, handleFile, isFile) {
  if (await handleFile(path, isFile || false) === false) return false;
  let files = fs.readdirSync(path);
  for (let item of files) {
    let tmpPath = path + '/' + item;
    let stats = fs.statSync(tmpPath);
    if (stats.isDirectory()) {
      await walk(tmpPath, handleFile, false);
    } else {
      if (await handleFile(tmpPath, true) === false) return false;
    }
  }
}

//递归创建
let mkdirs = async function (filePath) {
  if (fs.existsSync(filePath)) return true;
  if (mkdirs(path.resolve(filePath, "../")) == false) {
    return false;
  }

  fs.mkdirSync(filePath);
  return true;
};

//移除, 不管是文件还是文件夹.
let rm = function (path, level) {
  level = level || 0;
  if (!level && !fs.existsSync(path)) return true;

  let stats = fs.statSync(path);
  if (stats.isFile()) {
    fs.unlinkSync(path);
    return true;
  }

  let files = fs.readdirSync(path);
  for (let item of files) {
    let tmpPath = path + '/' + item;
    stats = fs.statSync(tmpPath);
    if (stats.isFile()) {
      fs.unlinkSync(tmpPath);
      continue;
    }
    if (rm(tmpPath, level + 1) == false) return false;
    fs.rmdirSync(tmpPath);
  }
  return true;
}

//递归监控
let watchsPath = async function (path) {
  fs.watch(path, (eventType, filename) => {
    if (filename)
      console.log(eventType + ":" + filename);
    // 输出: <Buffer ...>
  });
}

//existsSync
// let existsFile = function (file) {
//   try {
//     fs.accessSync(file, fs.constants.F_OK)
//     return true;
//   }
//   catch (e) {
//     return false;
//   }
// }

//处理定义的变量。 <<define-test>>
let procDefine = function (content, defineMap) {

  Object.keys(defineMap).forEach(key => {
    content = content.replace("<<define-" + key + ">>", defineMap[key], "g")
  });

  return content;
}

//核心，处理tag
let procTag = function (content, allTagNames, language) {
  //先保留当前读言。
  var allTags = allTagNames.map(it => "<" + it + ">");
  var currentLanguageTag = "<" + language + ">";


  var procCurrentLang = function (index, tagName) {
    var indexObj = content.indexOfExt(index, true, "</" + tagName + ">");
    if (!indexObj) return false;

    content = content.slice(0, index) + content.slice(index + tagName.length + 2, indexObj.stringIndex) + content.slice(indexObj.stringIndex + tagName.length + 3);
    return true;
  }

  var removeTag = function (index, tagName) {
    var indexObj = content.indexOfExt(index, true, "</" + tagName + ">");
    if (!indexObj) return false;

    content = content.slice(0, index) + content.slice(indexObj.stringIndex + tagName.length + 3);
    return true;
  }


  var prevIndex = 0;
  while (true) {
    var indexObj = content.indexOfExt(prevIndex, true, "<ce");
    if (!indexObj) break;

    prevIndex = indexObj.stringIndex;

    var nextChar = content[indexObj.stringIndex + 3];
    if (nextChar != ' ' && nextChar != '\t' && nextChar != '/' && nextChar != '>') {
      prevIndex += 3;
      continue;
    }

    var tagEndIndexObj = content.indexOfExt(indexObj.stringIndex + 2, false, "/>", ">");
    if (!tagEndIndexObj) {
      break;
    }

    //
    var prevIndexObj = content.indexOfPrevExt(indexObj.stringIndex, false, ">", "'", "\"");
    if (!prevIndexObj) {
      //如果找不到，则认为是在最开始。
      prevIndexObj = {stringIndex: -1, matchedValue: ">", matchedIndex: 0};
    }


    //ce结束的索引，一般指向 >的位置。
    var ceEndIndex = 0;
    if (tagEndIndexObj.matchedValue == "/>") {
      ceEndIndex = tagEndIndexObj.stringIndex + 1;
    }
    else { //匹配 >
      if (prevIndexObj.matchedValue == ">") {
        var tagEndIndexObj2 = content.indexOfExt(tagEndIndexObj.stringIndex, false, "</ce>")
        if (!tagEndIndexObj2) {
          ceEndIndex = tagEndIndexObj.stringIndex;
        }
        else {
          ceEndIndex = tagEndIndexObj2.stringIndex + 4;
        }
      }
      else {//如果在字符串里
        var tagEndIndexObj2 = content.indexOfExt(tagEndIndexObj.stringIndex, false, "</ce>", prevIndexObj.matchedValue)
        if (!tagEndIndexObj2) {
          ceEndIndex = tagEndIndexObj.stringIndex;
        }
        else if (tagEndIndexObj2.matchedValue == prevIndexObj.matchedValue) {
          ceEndIndex = tagEndIndexObj.stringIndex
        }
        else {
          ceEndIndex = tagEndIndexObj2.stringIndex + 3;
        }
      }
    }


    var endIndexObj

    if (prevIndexObj.matchedValue == ">") {
      endIndexObj = content.indexOfExt(ceEndIndex, false, "<");
    }
    else {
      endIndexObj = content.indexOfExt(ceEndIndex, false, prevIndexObj.matchedValue);
    }

    if (!endIndexObj) {
      endIndexObj = {stringIndex: content.length, matchedValue: "<", matchedIndex: 0}
    }

    content = content.slice(0, prevIndexObj.stringIndex + 1) +
        "<c>" + content.slice(prevIndexObj.stringIndex + 1, indexObj.stringIndex) + "</c><e>" +
        content.slice(ceEndIndex + 1, endIndexObj.stringIndex) + "</e>" + content.slice(endIndexObj.stringIndex)

    prevIndex = endIndexObj.stringIndex;
  }


  prevIndex = 0;
  while (true) {
    var indexObj = content.indexOfExt(prevIndex, true, allTags);
    if (!indexObj) break;

    prevIndex = indexObj.stringIndex;

    if (indexObj.matchedValue == currentLanguageTag) {
      if (procCurrentLang(indexObj.stringIndex, language) == false) {
        prevIndex++;
      }
      continue;
    }


    if (removeTag(indexObj.stringIndex, allTagNames[indexObj.matchedIndex]) == false) {
      prevIndex++;
    }
  }

  return content;
}

let copyFile = async function (src, target) {
  return new Promise((accept, reject) => {

    var fileWriteStream = fs.createWriteStream(target);
    fs.createReadStream(src).pipe(fileWriteStream);

    fileWriteStream.on('close', function () {
      accept();
    });
  });
}

exports.AsBool = AsBool;
exports.walkFile = walk;
exports.mkdirs = mkdirs;
exports.rm = rm;
exports.copyFile = copyFile
//exports.existsFile = existsFile
exports.procTagLang = procTag
exports.procDefine = procDefine
exports.watchsPath = watchsPath

